package cn.aiyuan.job;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * @program: myUtil
 * @description: 工具类 - 树状结构生成
 * @author: syf
 * @create: 2021-06-15 10:38
 */
public class TreeModel<T> {

    /**
     * 上级字段名称
     */
    private Function<T, ?> superColumn;

    /**
     * 当前比对字段
     */
    private Function<T, ?> column;

    /**
     * 列表接收字段名
     */
    private BiConsumer<T, List<T>> children;

    /**
     * 数据列表
     */
    private List<T> list;

    /**
     * 数据解除关联性使用的对象
     */
    private Supplier<T> decoupling;

    /**
     * 私有化工具类
     */
    private TreeModel() {
    }

    /**
     * 初始化载入树状工具类
     *
     * @param list        需要整合的数据列表
     * @param superColumn 上级字段(对象::字段get方法)
     * @param column      本级字段(对象::字段get方法)
     * @param children    下级数据存储字段(对象::字段set方法)
     * @return 树状工具类
     */
    public static <R> TreeModel<R> load(List<R> list, Function<R, ?> superColumn,
                                        Function<R, ?> column, BiConsumer<R, List<R>> children) {
        TreeModel<R> treeModel = new TreeModel<>();
        treeModel.setSuperColumn(superColumn)
                .setColumn(column)
                .setChildren(children)
                .setList(list);
        return treeModel;
    }

    /**
     * 获取树状数据（不解除原集合中对象与树状集合对象的关联）
     *
     * @param initValue 根数据
     * @return 树状数据数据列表
     */
    public List<T> getTree(Object initValue) {
        List<T> tree = treeAll(initValue);
        if (tree != null && tree.size() > 0) {
            return tree.stream().filter(ls -> initValue.equals(superColumn.apply(ls))).collect(Collectors.toList());
        }
        return tree;
    }

    /**
     * 获取树状数据（解除原集合中对象与树状集合对象的关联）
     *
     * @param initValue  根数据
     * @param decoupling 解除关联对象（对象::new）
     * @return 树状数据数据列表
     */
    public List<T> getTree(Object initValue, Supplier<T> decoupling) {
        this.setDecoupling(decoupling);
        return tree(initValue);
    }

    /**
     * 获取树状数据
     *
     * @param initValue 根数据
     * @return 树状数据
     */
    private List<T> treeAll(Object initValue) {
        if (list == null || list.size() < 1) {
            return list;
        }
        List<T> collect = list.stream().filter(f -> initValue.equals(superColumn.apply(f))).collect(Collectors.toList());
        if (collect.size() < 1) {
            return null;
        }
        collect.forEach(c -> children.accept(c, treeAll(column.apply(c))));
        return collect;
    }

    private List<T> tree(Object o) {
        List<T> childrenList = new ArrayList<>();
        if(o == null){
            return childrenList;
        }
        for (T entity : list) {
            if (o.equals(superColumn.apply(entity))) {
                T now = decoupling.get();
                copy(entity, now);
                childrenList.add(now);
            }
        }
        for (T cs : childrenList) {
            children.accept(cs, tree(column.apply(cs)));
        }
        return childrenList;
    }

    /**
     * 将source中的属性赋值给target
     * @param source    数据来源类
     * @param target    数据接收类
     */
    private void copy(T source, T target) {
        if (source == null) {
            throw new NullPointerException("dataSource");
        }
        if (target == null) {
            target = decoupling.get();
        }
        Field[] sourceFields = source.getClass().getDeclaredFields();
        Field[] targetFields = target.getClass().getDeclaredFields();
        Map<String, Object> sourceMap = new HashMap<>(sourceFields.length);
        try {
            for (Field field : sourceFields) {
                if (Modifier.isFinal(field.getModifiers())) {
                    continue;
                }
                field.setAccessible(true);
                sourceMap.put(field.getName(), field.get(source));

            }
            for (Field field : targetFields) {
                Object o = sourceMap.get(field.getName());
                if (o == null) {
                    continue;
                }
                field.setAccessible(true);
                field.set(target, o);
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private TreeModel<T> setSuperColumn(Function<T, ?> superColumn) {
        if (superColumn == null) {
            throw new NullPointerException("superColumn");
        }
        this.superColumn = superColumn;
        return this;
    }

    private TreeModel<T> setColumn(Function<T, ?> column) {
        if (column == null) {
            throw new NullPointerException("column");
        }
        this.column = column;
        return this;
    }

    private TreeModel<T> setChildren(BiConsumer<T, List<T>> children) {
        if (children == null) {
            throw new NullPointerException("children");
        }
        this.children = children;
        return this;
    }

    private void setList(List<T> list) {
        if (list == null || list.size() < 1) {
            throw new NullPointerException("list");
        }
        this.list = list;
    }

    private void setDecoupling(Supplier<T> decoupling) {
        if (decoupling == null) {
            throw new NullPointerException("decoupling");
        }
        this.decoupling = decoupling;
    }

}

